/**
 * 
 */
package org.dragonfire.dasm.directives;

import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Stack;

import org.dragonfire.dasm.Command;
import org.dragonfire.dasm.Listing;
import org.dragonfire.dasm.DirectiveManager.Directive;
import org.dragonfire.parser.Equation;
import org.dragonfire.parser.functions.Function;

/**
 * Implements a stack data structure that allows for data to be stored in a FILO
 * (first in, last out) manner. Stacks may be named to maintain multiple
 * separate stacks<br />
 * <br />
 * Format:<br />
 * <code>#PUSH [<i>&lt;stackName&gt;</i>,]<i>&lt;data1&gt;</i>[,<i>&lt;data2&gt;</i>[,...]]</code><br />
 * <br />
 * Parameters:<br />
 * <table>
 * <tr>
 * <td><code>stackName</code></td>
 * <td>(String, Optional) The name of the stack on which to push the data. If
 * this parameter is omitted, then {@link #DEFAULT_STACK} is used and <b>only
 * one data value is permitted</b>.</td>
 * </tr>
 * <tr>
 * <td><code>data1</code></td>
 * <td>The value to push onto the stack. If multiple data parameters are
 * passed, then <code>stackName</code> must also be passed.</td>
 * </tr>
 * </table><br />
 * Errors:<br />
 * <table>
 * <tr>
 * <td><code>ERROR_STRING_REQUIRED</code></td>
 * <td>Thrown if <code>stackName</code> is not a string</td>
 * </tr>
 * </table>
 * 
 * @author Darth Android
 * @version 2.0
 * @see Pop
 * @see Stack
 */
public class Push extends Directive {

	/**
	 * The name of the stack to use when one isn't specified
	 */
	public static final String DEFAULT_STACK = "";
	/**
	 * Stack data
	 */
	private HashMap<String, Stack<String>> data = new HashMap<String, Stack<String>>();

	/**
	 * Default constructor registers the <code>$pop</code> function
	 */
	public Push() {
		Function.putFunction("pop", new Pop());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ArrayList<Listing> pass1(Command c) {
		ArrayList<Listing> list = new ArrayList<Listing>();
		// start with the default stack name
		String stackName = DEFAULT_STACK;
		// loop through all params
		for (int i = 0; i < c.getParams().length; i++) {
			String param = c.getParam(i);
			// if more than one parameters, then the first is the stack name
			if (c.getParams().length > 1 && i == 0)
				stackName = evalString(param);
			else {
				// else push the parameter onto the stack
				list.add(new Listing(0, c));
				// create a new stack if one doesn't already exist
				if (!data.containsKey(stackName))
					data.put(stackName, new Stack<String>());
				data.get(stackName).push(param);
			}
		}
		return list;
	}

	/**
	 * Removes the head of a named stack
	 * 
	 * @param stackName
	 *            The name of the stack to retrieve data from
	 * @return The top of the stack
	 * @throws EmptyStackException
	 *             If the stack is empty
	 * @throws IllegalArgumentException
	 *             If the stack doesn't exist (read: hasn't been created by
	 *             <code>#PUSH</code> yet)
	 */
	public String getData(String stackName) {
		if (!data.containsKey(stackName))
			throw new IllegalArgumentException("No stack named '" + stackName + "'.");
		return data.get(stackName).pop();
	}

	/**
	 * Determines if a stack has at least one element
	 * 
	 * @param stackName
	 *            The name of the stack
	 * @return <code>true</code> if the stack is not empty<br />
	 *         <code>false</code> if the stack is empty
	 * @see #getData(String)
	 */
	public boolean hasData(String stackName) {
		if (data.containsKey(stackName) == false)
			return false;
		return !data.get(stackName).isEmpty();
	}

	/**
	 * Removes the head of the named stack (or default stack if a name is not
	 * given) and returns it. If the stack is empty, then a value of
	 * <code>0</code> is returned.<br />
	 * <br />
	 * Format:<br />
	 * <code>$pop([<i>&lt;stackName&gt;</i>])</code><br />
	 * <br />
	 * Parameters:<br />
	 * <table>
	 * <tr>
	 * <td><code>stackName</code></td>
	 * <td>(String, Optional) The name of the stack from which to pop the data.
	 * If this parameter is omitted, then {@link #DEFAULT_STACK} is used.</td>
	 * </tr>
	 * </table><br />
	 * <br />
	 * Errors:<br />
	 * <table>
	 * <tr>
	 * <td><code>ERROR_STRING_REQUIRED</code></td>
	 * <td>Thrown if <code>stackName</code> is not a string</td>
	 * </tr>
	 * </table>
	 * 
	 * @see Push
	 * @version 2.0
	 * @author Darth Android
	 */
	public class Pop extends Function {

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int minParams() {
			return 0;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public String eval(Equation equ, String[] args) {
			String stackName = DEFAULT_STACK;
			if (args.length > 0)
				stackName = evalString(args[0]);
			// return the head if the stack isn't empty
			if (hasData(stackName))
				return getData(stackName);
			return "0";
		}
	}
}
